package com.gg.registration.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gg.registration.enums.PaymentStatusEnum;
import com.gg.registration.enums.PaymentTypeEnum;
import com.gg.registration.enums.RefundStatusEnum;
import com.gg.registration.model.order.OrderInfo;
import com.gg.registration.model.order.PaymentInfo;
import com.gg.registration.model.order.RefundInfo;
import com.gg.registration.order.service.OrderService;
import com.gg.registration.order.service.PaymentInfoService;
import com.gg.registration.order.service.RefundInfoService;
import com.gg.registration.order.service.WechatPayService;
import com.gg.registration.order.utils.HttpClient;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Author：  chenjunjia
 * Date：    2022/4/27 21:19
 * WeChat：  China_JoJo_
 */
@Service
public class WechatPayServiceImpl implements WechatPayService {

    @Resource
    private PaymentInfoService paymentInfoService;

    private OrderServiceImpl orderService = new OrderServiceImpl();

//    @Resource
//    private OrderService orderService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RefundInfoService refundInfoService;

    //关联的公众号appid
    private String appid = "wx74862e0dfcf69954";
    //商户号
    private String partner = "1558950191";
    //商户key
    private String partnerkey = "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb";


    /**
     * 生成二维码所需参数
     *
     * @param orderId 订单号
     * @return 返回hashmap携带数据
     */
    @Override
    public Map createNative(Long orderId) {
        Map<String, String> resultMap = null;

        //先去redis查，有就返回
        /**
         * 一个 bug，用户在第一次扫码的时候，若退出了，后面的 redis 生成了 key，但没支付
         * 到这里这个判断时就会直接返回，而不生成二维码
         * 要等到 key 过期了才能生成下一次的二维码
         *
         * 解决方案：
         * 看看在什么地方完成支付之后销毁 redis 的 key
         * 或者关闭未支付但关闭了二维码的时候销毁 redis 中的 key
         */
        HashMap payMap = (HashMap) redisTemplate.opsForValue().get("com.gg.registration.order.service.WechatPayService.createNative.orderId:" + orderId.toString());
        if (null != payMap) {
            // 04.27 应该在这里判断消费记录以及订单的状态才行，若完成了订单则进行销毁key操作
            // 04.28 应该在完成支付的微信接口中进行销毁key操作，而不是在这里
//            System.out.println("redis里的:"+payMap);
            return payMap;
        }
        //用订单编号查订单
        OrderInfo orderInfo = orderService.getById(orderId);
        //保存支付记录
        paymentInfoService.savePaymentInfo(orderInfo, PaymentTypeEnum.WEIXIN.getStatus());
        //封装微信api所需参数
        HashMap paramMap = new HashMap();
        paramMap.put("appid", appid);
        paramMap.put("mch_id", partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
        String body = orderInfo.getReserveDate() + "就诊" + orderInfo.getDepname();
        paramMap.put("body", body);
        //paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
        paramMap.put("total_fee", "1");//一分钱
        paramMap.put("spbill_create_ip", "127.0.0.1");
        paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
        paramMap.put("trade_type", "NATIVE");
        //调用HttpClient向微信第三方接口固定地址发请求
        HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
        try {
            //设置所需的xml参数，使用微信SDK自带的工具包来转换
            httpClient.setXmlParam(WXPayUtil.generateSignedXml(paramMap, partnerkey));
            //开启https
            httpClient.setHttps(true);
            //发起请求
            httpClient.post();
            //请求后返回数据
            String xml = httpClient.getContent();
            //微信工具将xml格式字符串转化成对象（未封装前端指定参数）
            resultMap = WXPayUtil.xmlToMap(xml);
//            System.out.println("微信返回的resultMap:"+resultMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //4、封装返回结果集（加上带有前端指定参数）
        Map map = resultMap;
        map.put("orderId", orderId);
        map.put("totalFee", orderInfo.getAmount());
        map.put("resultCode", resultMap.get("result_code"));
        map.put("codeUrl", resultMap.get("code_url"));
//        System.out.println("封装后加到redis中的map:"+map);
        //添加到redis，限时两小时
        if (null != resultMap) {
            //将微信返回的数据加到redis中，限时两小时
            /**
             * 这个没带有前端指定参数，所以在“redis查，有就返回”这个步骤时，没返回带有前端参数的map，所以前端二维码不能重复显示
             */
//            redisTemplate.opsForValue().set("com.gg.registration.order.service.WechatPayService.createNative.orderId:"+orderId.toString(),resultMap,2, TimeUnit.HOURS);
            //现在封装带有前端参数的map
            redisTemplate.opsForValue().set("com.gg.registration.order.service.WechatPayService.createNative.orderId:" + orderId.toString(), map, 2, TimeUnit.HOURS);
        }
        return map;
    }

    @Override
    public Map<String, String> queryPayStatus(Long orderId, Integer status) throws Exception {
        OrderInfo orderInfo = orderService.getById(orderId);
        // 1、封装参数
        Map paramMap = new HashMap<>();
        paramMap.put("appid", appid);
        paramMap.put("mch_id", partner);
        paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        // 2、设置请求（微信专用固定的查询支付状态的地址）
        HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
        client.setHttps(true);
        client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, partnerkey));
        // 发起请求
        client.post();
        // 3、返回第三方的数据，转成Map
        String xml = client.getContent();
        Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);

        return resultMap;
    }

    /**
     * 退款
     *
     * @param orderId
     * @return
     */
    @Override
    public Boolean refund(Long orderId) throws Exception {
        //先获取消费记录信息，里面有对外业务编号、订单id、交易编号、支付金额、支付状态等信息
        PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(orderId, PaymentTypeEnum.WEIXIN.getStatus());
        //保存退款记录
        RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);
        //看看退款记录中是否完成退款，若完成退款则直接返回
        if (RefundStatusEnum.REFUND.getStatus() == refundInfo.getRefundStatus()) {
            return true;
        }
        //将退款参数加到map中去发送给微信方进行退款操作
        Map<String, String> paramMap = new HashMap<>();
        //封装请求参数
        paramMap.put("appid", appid);       //公众账号ID
        paramMap.put("mch_id", partner);   //商户编号
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("transaction_id", paymentInfo.getTradeNo()); //微信订单号
        paramMap.put("out_trade_no", paymentInfo.getOutTradeNo()); //商户订单编号
        paramMap.put("out_refund_no", "tk" + paymentInfo.getOutTradeNo()); //商户退款单号
        paramMap.put("total_fee", "1");
        paramMap.put("refund_fee", "1");
        //设置请求
        String paramXml = WXPayUtil.generateSignedXml(paramMap, partnerkey);
        //以后的路径可以从微信提供的常量类中拿，每个功能都对应不同的地址
        HttpClient client = new HttpClient(WXPayConstants.REFUND_URL);
        client.setXmlParam(paramXml);
        client.setHttps(true);
        //证书
        client.setCert(true);
        client.setCertPassword(partner);
        client.post();
        //第三方返回的数据
        String xml = client.getContent();
        Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
        if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
            //若微信那边退款成功了，更新退款信息表，将状态改成已退款
            refundInfo.setCallbackTime(new Date());
            refundInfo.setTradeNo(resultMap.get("refund_id"));
            //在这里才将退款信息修改成: 已退款
            refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
            refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
            //修改退款信息
            refundInfoService.updateById(refundInfo);
            return true;
        }
        return false;
    }
}
